﻿using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;

using System.Threading;

namespace Gadgeteer.Modules.GHIElectronics
{
    // -- CHANGE FOR MICRO FRAMEWORK 4.2 --
    // If you want to use Serial, SPI, or DaisyLink (which includes GTI.SoftwareI2C), you must do a few more steps
    // since these have been moved to separate assemblies for NETMF 4.2 (to reduce the minimum memory footprint of Gadgeteer)
    // 1) add a reference to the assembly (named Gadgeteer.[interfacename])
    // 2) in GadgeteerHardware.xml, uncomment the lines under <Assemblies> so that end user apps using this module also add a reference.

    /// <summary>
    /// A Stepper_L6470 module for Microsoft .NET Gadgeteer
    /// </summary>
    public class Stepper_L6470 : GTM.Module
    {
        private GT.Interfaces.SPI.Configuration spiConfig;
        private GT.Interfaces.SPI spi;

        private GT.Interfaces.DigitalInput busyPin;
        private GT.Interfaces.DigitalOutput resetPin;
		/// <summary>
		/// The step clock.
		/// </summary>
        public GT.Interfaces.DigitalOutput stepClock;

        private RegsStruct regsStruct;

        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public Stepper_L6470(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            socket.EnsureTypeIsSupported('S', this);

            // Initialize SPI
            spiConfig = new GTI.SPI.Configuration(false, 1000, 1000, true, true, 5000);
            spi = new GTI.SPI(socket, spiConfig, GTI.SPI.Sharing.Shared, socket, Socket.Pin.Six, this);

            // Initialize pins
            busyPin = new GTI.DigitalInput(socket, Socket.Pin.Three, GTI.GlitchFilterMode.Off, GTI.ResistorMode.PullUp, this);
            resetPin = new GTI.DigitalOutput(socket, Socket.Pin.Four, true, this);
            stepClock = new GTI.DigitalOutput(socket, Socket.Pin.Five, false, this);

            // Initialize chip registers
            InitializeChip();

            Reset();
        }

        private void InitializeChip()
        {
            regsStruct = new RegsStruct();
            Regs_Struct_Reset(regsStruct);

            /* Acceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
            regsStruct.ACC = AccDec_Steps_to_Par(466);
            /* Deceleration rate settings to 466 steps/s2, range 14.55 to 59590 steps/s2 */
            regsStruct.DEC = AccDec_Steps_to_Par(466);
            /* Maximum speed settings to 488 steps/s, range 15.25 to 15610 steps/s */
            regsStruct.MAX_SPEED = MaxSpd_Steps_to_Par(488);
            /* Minimum speed settings to 0 steps/s, range 0 to 976.3 steps/s */
            regsStruct.MIN_SPEED = MinSpd_Steps_to_Par(0);
            /* Full step speed settings 252 steps/s, range 7.63 to 15625 steps/s */
            regsStruct.FS_SPD = FSSpd_Steps_to_Par(252);
            /* Hold duty cycle (torque) settings to 10%, range 0 to 99.6% */
            regsStruct.KVAL_HOLD = Kval_Perc_to_Par(10);
            /* Run duty cycle (torque) settings to 10%, range 0 to 99.6% */
            regsStruct.KVAL_RUN = Kval_Perc_to_Par(10);
            /* Acceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
            regsStruct.KVAL_ACC = Kval_Perc_to_Par(10);
            /* Deceleration duty cycle (torque) settings to 10%, range 0 to 99.6% */
            regsStruct.KVAL_DEC = Kval_Perc_to_Par(10);
            /* Intersect speed settings for BEMF compensation to 200 steps/s, range 0 to 3906 steps/s */
            regsStruct.INT_SPD = IntSpd_Steps_to_Par(200);
            /* BEMF start slope settings for BEMF compensation to 0.038% step/s, range 0 to 0.4% s/step */
            regsStruct.ST_SLP = BEMF_Slope_Perc_to_Par((float)0.038);
            /* BEMF final acc slope settings for BEMF compensation to 0.063% step/s, range 0 to 0.4% s/step */
            regsStruct.FN_SLP_ACC = BEMF_Slope_Perc_to_Par((float)0.063);
            /* BEMF final dec slope settings for BEMF compensation to 0.063% step/s, range 0 to 0.4% s/step */
            regsStruct.FN_SLP_DEC = BEMF_Slope_Perc_to_Par((float)0.063);
            /* Thermal compensation param settings to 1, range 1 to 1.46875 */
            regsStruct.K_THERM = KTherm_to_Par(1);
            /* Overcurrent threshold settings to 1500mA */
            regsStruct.OCD_TH = (byte)Overcurrent_Detection_Threshold.OCD_TH_1500mA;
            /* Stall threshold settings to 1000mA, range 31.25 to 4000mA */
            regsStruct.STALL_TH = StallTh_to_Par(1000);
            /* Step mode settings to 128 microsteps */
            regsStruct.STEP_MODE = (byte)Step_Select.STEP_SEL_1_128;
            /* Alarm settings - all alarms enabled */
            regsStruct.ALARM_EN = (byte)Alarm_Enable.ALARM_EN_OVERCURRENT | (byte)Alarm_Enable.ALARM_EN_THERMAL_SHUTDOWN
                | (byte)Alarm_Enable.ALARM_EN_THERMAL_WARNING | (byte)Alarm_Enable.ALARM_EN_UNDER_VOLTAGE | (byte)Alarm_Enable.ALARM_EN_STALL_DET_A
                | (byte)Alarm_Enable.ALARM_EN_STALL_DET_B | (byte)Alarm_Enable.ALARM_EN_SW_TURN_ON | (byte)Alarm_Enable.ALARM_EN_WRONG_NPERF_CMD;
            /* Internal oscillator, 2MHz OSCOUT clock, supply voltage compensation disabled, *
             * overcurrent shutdown enabled, slew-rate = 290 V/us, PWM frequency = 15.6kHz   */
            regsStruct.CONFIG = (ushort)CONFIG_OSC_MGMT.CONFIG_INT_16MHZ_OSCOUT_2MHZ | (ushort)CONFIG_SW_MODE_TypeDef.CONFIG_SW_HARD_STOP
                | (ushort)CONFIG_EN_VSCOMP.CONFIG_VS_COMP_DISABLE | (ushort)CONFIG_OC_SD.CONFIG_OC_SD_ENABLE | (ushort)CONFIG_POW_SR.CONFIG_SR_290V_us
                | (ushort)CONFIG_F_PWM_INT.CONFIG_PWM_DIV_2 | (ushort)CONFIG_F_PWM_DEC.CONFIG_PWM_MUL_1;

            /* Program all dSPIN registers */
            SetRegisters(regsStruct);
        }

        /// <summary>
        /// Resets the module
        /// </summary>
        public void Reset()
        {
            resetPin.Write(false);
            Thread.Sleep(500);
            resetPin.Write(true);
            Thread.Sleep(1000);
        }

        private byte WriteByte(byte data)
        {
            byte[] send = new byte[] { data };
            byte[] receive = new byte[1];

            // LOOK HERE IF PROBLEMS
            //spi.WriteRead(send, 0, 1, receive, 0, 1, 0);
            spi.WriteRead(send, receive);

            return receive[0];
        }

        /// <summary>
        /// Send NOP operation code to the module
        /// </summary>
        public void NOP()
        {
            WriteByte((byte)Commands.NOP);
        }

        /// <summary>
        /// Sets a parameter to the passed in value
        /// </summary>
        /// <param name="param">Parameter to set</param>
        /// <param name="value">Value to set</param>
        public void SetParam(Registers param, uint value)
        {
            WriteByte((byte)(((byte)(Commands.SET_PARAM) | (byte)(param))));
            switch (param)
            {
                case Registers.ABS_POS:
                case Registers.MARK:
                case Registers.SPEED:
                    /* Send parameter - byte 2 to dSPIN */
                    WriteByte((byte)(value >> 16));
                    WriteByte((byte)(value >> 8));
                    WriteByte((byte)(value));
                    break;
                case Registers.ACC:
                case Registers.DEC:
                case Registers.MAX_SPEED:
                case Registers.MIN_SPEED:
                case Registers.FS_SPD:
                case Registers.INT_SPD:
                case Registers.CONFIG:
                case Registers.STATUS:
                    /* Send parameter - byte 1 to dSPIN */
                    WriteByte((byte)(value >> 8));
                    WriteByte((byte)(value));
                    break;
                default:
                    /* Send parameter - byte 0 to dSPIN */
                    WriteByte((byte)(value));
                    break;
            }
        }

        /// <summary>
        /// Get a value for a given parameter
        /// </summary>
        /// <param name="param">Parameter to retrieve</param>
        /// <returns></returns>
        public uint GetParam(Registers param)
        {
            uint temp = 0;
            uint rx = 0;

            /* Send GetParam operation code to dSPIN */
            temp = WriteByte((byte)((byte)Commands.GET_PARAM | (byte)param));
            /* MSB which should be 0 */
            temp = temp << 24;
            rx |= temp;
            switch (param)
            {
                case Registers.ABS_POS:
                case Registers.MARK:
                case Registers.SPEED:
                    temp = WriteByte((byte)(0x00));
                    temp = temp << 16;
                    rx |= temp;
                    temp = WriteByte((byte)(0x00));
                    temp = temp << 8;
                    rx |= temp;
                    temp = WriteByte((byte)(0x00));
                    rx |= temp;
                    break;
                case Registers.ACC:
                case Registers.DEC:
                case Registers.MAX_SPEED:
                case Registers.MIN_SPEED:
                case Registers.FS_SPD:
                case Registers.INT_SPD:
                case Registers.CONFIG:
                case Registers.STATUS:
                    temp = WriteByte((byte)(0x00));
                    temp = temp << 8;
                    rx |= temp;
                    temp = WriteByte((byte)(0x00));
                    rx |= temp;
                    break;
                default:
                    temp = WriteByte((byte)(0x00));
                    rx |= temp;
                    break;
            }
            return rx;
        }

        /// <summary>
        /// Runs the motor
        /// </summary>
        /// <param name="direction">Direction of the motor</param>
        /// <param name="speed">Speed of the motor</param>
        public void Run(Direction direction, uint speed)
        {
            /* Send RUN operation code to dSPIN */
            WriteByte((byte)((byte)(Commands.RUN) | (byte)(direction)));
            /* Send speed - byte 2 data dSPIN */
            WriteByte((byte)(speed >> 16));
            /* Send speed - byte 1 data dSPIN */
            WriteByte((byte)(speed >> 8));
            /* Send speed - byte 0 data dSPIN */
            WriteByte((byte)(speed));
        }

        /// <summary>
        /// Steps the motor while the stepClock pin is high
        /// </summary>
        /// <param name="direction">Direction of the motor</param>
        public void StepClock(Direction direction)
        {
            /* Send StepClock operation code to dSPIN */
            WriteByte((byte)((byte)(Commands.STEP_CLOCK) | (byte)(direction)));
        }

        /// <summary>
        /// Moves the motor in the passed in direction for the passed in steps
        /// </summary>
        /// <param name="direction">Direction to move the motor</param>
        /// <param name="n_step">Number of steps to move</param>
        public void Move(Direction direction, uint n_step)
        {
            /* Send Move operation code to dSPIN */
            WriteByte((byte)((byte)Commands.MOVE | (byte)direction));
            /* Send n_step - byte 2 data dSPIN */
            WriteByte((byte)(n_step >> 16));
            /* Send n_step - byte 1 data dSPIN */
            WriteByte((byte)(n_step >> 8));
            /* Send n_step - byte 0 data dSPIN */
            WriteByte((byte)(n_step));
        }

        /// <summary>
        /// Sets the motor to a specific position
        /// </summary>
        /// <param name="abs_pos">Absolute position to move to</param>
        public void GoToPosition(uint abs_pos)
        {
            /* Send GoTo operation code to dSPIN */
            WriteByte((byte)Commands.GO_TO);
            /* Send absolute position parameter - byte 2 data to dSPIN */
            WriteByte((byte)(abs_pos >> 16));
            /* Send absolute position parameter - byte 1 data to dSPIN */
            WriteByte((byte)(abs_pos >> 8));
            /* Send absolute position parameter - byte 0 data to dSPIN */
            WriteByte((byte)(abs_pos));
        }

        /// <summary>
        /// Moves to a specific position, going in the passed in direction
        /// </summary>
        /// <param name="direction">Direction to move</param>
        /// <param name="abs_pos">Absolute position to move to</param>
        public void GoToDirection(Direction direction, uint abs_pos)
        {
            /* Send GoTo_DIR operation code to dSPIN */
            WriteByte((byte)((byte)Commands.GO_TO_DIR | (byte)direction));
            /* Send absolute position parameter - byte 2 data to dSPIN */
            WriteByte((byte)(abs_pos >> 16));
            /* Send absolute position parameter - byte 1 data to dSPIN */
            WriteByte((byte)(abs_pos >> 8));
            /* Send absolute position parameter - byte 0 data to dSPIN */
            WriteByte((byte)(abs_pos));
        }

        /// <summary>
        /// Moves the motor until a specific action happens
        /// </summary>
        /// <param name="action">Action to stop on</param>
        /// <param name="direction">Direction to move</param>
        /// <param name="speed">Speed to move in</param>
        public void GoUntil(Action action, Direction direction, uint speed)
        {
            /* Send GoUntil operation code to dSPIN */
            WriteByte((byte)((byte)Commands.GO_UNTIL | (byte)action | (byte)direction));
            /* Send speed parameter - byte 2 data to dSPIN */
            WriteByte((byte)(speed >> 16));
            /* Send speed parameter - byte 1 data to dSPIN */
            WriteByte((byte)(speed >> 8));
            /* Send speed parameter - byte 0 data to dSPIN */
            WriteByte((byte)(speed));
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <param name="action"></param>
        /// <param name="direction"></param>
        public void ReleaseSW(Action action, Direction direction)
        {
            /* Send ReleaseSW operation code to dSPIN */
            WriteByte((byte)((byte)Commands.RELEASE_SW | (byte)action | (byte)direction));
        }

        /// <summary>
        /// Moves the motor to the home position
        /// </summary>
        public void GoHome()
        {
            /* Send GoHome operation code to dSPIN */
            WriteByte((byte)Commands.GO_HOME);
        }

        /// <summary>
        /// Goes the motor to the mark position
        /// </summary>
        public void GoMark()
        {
            /* Send GoMark operation code to dSPIN */
            WriteByte((byte)Commands.GO_MARK);
        }

        /// <summary>
        /// Resets the motor to the position
        /// </summary>
        public void ResetPos()
        {
            /* Send ResetPos operation code to dSPIN */
            WriteByte((byte)Commands.RESET_POS);
        }

        /// <summary>
        /// Resets the device
        /// </summary>
        public void ResetDevice()
        {
            /* Send ResetDevice operation code to dSPIN */
            WriteByte((byte)Commands.RESET_DEVICE);
        }

        /// <summary>
        /// Soft stop operation
        /// </summary>
        public void SoftStop()
        {
            /* Send SoftStop operation code to dSPIN */
            WriteByte((byte)Commands.SOFT_STOP);
        }

        /// <summary>
        /// Hard stop operation
        /// </summary>
        public void HardStop()
        {
            /* Send HardStop operation code to dSPIN */
            WriteByte((byte)Commands.HARD_STOP);
        }

        /// <summary>
        /// TODO
        /// </summary>
        public void SoftHiZ()
        {
            /* Send SoftHiZ operation code to dSPIN */
            WriteByte((byte)Commands.SOFT_HIZ);
        }

        /// <summary>
        /// TODO
        /// </summary>
        public void HardHiZ()
        {
            /* Send HardHiZ operation code to dSPIN */
            WriteByte((byte)Commands.HARD_HIZ);
        }

        /// <summary>
        /// Gets the status
        /// </summary>
        /// <returns>Status</returns>
        public ushort GetStatus()
        {
            ushort temp = 0;
            ushort rx = 0;

            /* Send GetStatus operation code to dSPIN */
            WriteByte((byte)Commands.GET_STATUS);
            /* Send zero byte / receive MSByte from dSPIN */
            temp = WriteByte((byte)(0x00));
            temp = (ushort)(temp << 8);
            rx |= temp;
            /* Send zero byte / receive LSByte from dSPIN */
            temp = WriteByte((byte)(0x00));
            rx |= temp;
            return rx;
        }

        /// <summary>
        /// Returns if the hardware is busy
        /// </summary>
        /// <returns>If the hardware is busy</returns>
        public bool BusyHW()
        {
            if (busyPin.Read() == false) return true;
            else
                //if(!(GPIO_ReadInputDataBit(BUSY_Port, BUSY_Pin))) return 0x01;
                //else return 0x00;
                //spi.
                return false;
        }

        /// <summary>
        /// Returns if the software is busy
        /// </summary>
        /// <returns>If the hardware is busy</returns>
        public byte BusySW()
        {
            if (0 == (GetStatus() & (ushort)Status_Masks.STATUS_BUSY)) return 0x01;
            else return 0x00;
        }
        
        /// <summary>
        /// Action Options
        /// </summary>
        public enum Action
        {
			/// <summary>
			/// The reset option.
			/// </summary>
            ACTION_RESET = ((byte)0x00),
            /// <summary>
            /// The copy option.
            /// </summary>
			ACTION_COPY = ((byte)0x01)
        };

        /// <summary>
        /// Direction Options
        /// </summary>
        public enum Direction
        {
			/// <summary>
			/// The forward option.
			/// </summary>
            FWD = ((byte)0x01),
			/// <summary>
			/// The reverse option.
			/// </summary>
            REV = ((byte)0x00)
        };

        /// <summary>
        /// Status Masks
        /// </summary>
        public enum Status_Masks
        {
			/// <summary>
			/// The HIZ status mask.
			/// </summary>
			STATUS_HIZ = (((ushort)0x0001)),
			/// <summary>
			/// The BUSY status mask.
			/// </summary>
			STATUS_BUSY = (((ushort)0x0002)),
			/// <summary>
			/// The SW_F status mask.
			/// </summary>
			STATUS_SW_F = (((ushort)0x0004)),
			/// <summary>
			/// The SW_EVN status mask.
			/// </summary>
			STATUS_SW_EVN = (((ushort)0x0008)),
			/// <summary>
			/// The DIR status mask.
			/// </summary>
			STATUS_DIR = (((ushort)0x0010)),
			/// <summary>
			/// The MOT_STATUS status mask.
			/// </summary>
			STATUS_MOT_STATUS = (((ushort)0x0060)),
			/// <summary>
			/// The NOTPERF_CMD status mask.
			/// </summary>
			STATUS_NOTPERF_CMD = (((ushort)0x0080)),
			/// <summary>
			/// The WRONG_CMD status mask.
			/// </summary>
			STATUS_WRONG_CMD = (((ushort)0x0100)),
			/// <summary>
			/// The OVLO status mask.
			/// </summary>
			STATUS_UVLO = (((ushort)0x0200)),
			/// <summary>
			/// The TH_WRN status mask.
			/// </summary>
			STATUS_TH_WRN = (((ushort)0x0400)),
			/// <summary>
			/// The TH_SD status mask.
			/// </summary>
			STATUS_TH_SD = (((ushort)0x0800)),
			/// <summary>
			/// The OCD status mask.
			/// </summary>
			STATUS_OCD = (((ushort)0x1000)),
			/// <summary>
			/// The STEP_LOSS_a status mask.
			/// </summary>
			STATUS_STEP_LOSS_A = (((ushort)0x2000)),
			/// <summary>
			/// The STATUS_STEP_LOSS_B status mask.
			/// </summary>
			STATUS_STEP_LOSS_B = (((ushort)0x4000)),
			/// <summary>
			/// The STATUS_SCK_MOD status mask.
			/// </summary>
            STATUS_SCK_MOD = (((ushort)0x8000))
        };

        /// <summary>
        /// Registers
        /// </summary>
        public enum Registers
        {
			/// <summary>
			/// The ABS_POS register.
			/// </summary>
			ABS_POS = ((byte)0x01),
			/// <summary>
			/// The EL_POS register.
			/// </summary>
			EL_POS = ((byte)0x02),
			/// <summary>
			/// The MARK register.
			/// </summary>
			MARK = ((byte)0x03),
			/// <summary>
			/// The SPEED register.
			/// </summary>
			SPEED = ((byte)0x04),
			/// <summary>
			/// The ACC register.
			/// </summary>
			ACC = ((byte)0x05),
			/// <summary>
			/// The DEC register.
			/// </summary>
			DEC = ((byte)0x06),
			/// <summary>
			/// The MAX_SPEED register.
			/// </summary>
			MAX_SPEED = ((byte)0x07),
			/// <summary>
			/// The MIN_SPEED register.
			/// </summary>
			MIN_SPEED = ((byte)0x08),
			/// <summary>
			/// The FS_SPD register.
			/// </summary>
			FS_SPD = ((byte)0x15),
			/// <summary>
			/// The KVAL_HOLD register.
			/// </summary>
			KVAL_HOLD = ((byte)0x09),
			/// <summary>
			/// The KVAL_RUN register.
			/// </summary>
			KVAL_RUN = ((byte)0x0A),
			/// <summary>
			/// The KVAL_ACC register.
			/// </summary>
			KVAL_ACC = ((byte)0x0B),
			/// <summary>
			/// The KVAL_DEC register.
			/// </summary>
			KVAL_DEC = ((byte)0x0C),
			/// <summary>
			/// The INT_SPD register.
			/// </summary>
			INT_SPD = ((byte)0x0D),
			/// <summary>
			/// The ST_SLP register.
			/// </summary>
			ST_SLP = ((byte)0x0E),
			/// <summary>
			/// The FN_SLP_ACC register.
			/// </summary>
			FN_SLP_ACC = ((byte)0x0F),
			/// <summary>
			/// The FN_SLP_DEC register.
			/// </summary>
			FN_SLP_DEC = ((byte)0x10),
			/// <summary>
			/// The K_THERM register.
			/// </summary>
			K_THERM = ((byte)0x11),
			/// <summary>
			/// The ADC_OUT register.
			/// </summary>
			ADC_OUT = ((byte)0x12),
			/// <summary>
			/// The OCD_TH register.
			/// </summary>
			OCD_TH = ((byte)0x13),
			/// <summary>
			/// The STALL_TH register.
			/// </summary>
			STALL_TH = ((byte)0x14),
			/// <summary>
			/// The STEP_MODE register.
			/// </summary>
			STEP_MODE = ((byte)0x16),
			/// <summary>
			/// The ALARM_EN register.
			/// </summary>
			ALARM_EN = ((byte)0x17),
			/// <summary>
			/// The CONFIG register.
			/// </summary>
			CONFIG = ((byte)0x18),
			/// <summary>
			/// The STATUS register.
			/// </summary>
			STATUS = ((byte)0x19),
			/// <summary>
			/// The RESERVED_REG1 register.
			/// </summary>
			RESERVED_REG1 = ((byte)0x1A),
			/// <summary>
			/// The RESERVED_REG2 register.
			/// </summary>
            RESERVED_REG2 = ((byte)0x1B)
        };

        /// <summary>
        /// Commands
        /// </summary>
        public enum Commands
		{
			/// <summary>
			/// The NOP command.
			/// </summary>
			NOP = ((byte)0x00),
			/// <summary>
			/// The SET_PARAM command.
			/// </summary>
			SET_PARAM = ((byte)0x00),
			/// <summary>
			/// The GET_PARAM command.
			/// </summary>
			GET_PARAM = ((byte)0x20),
			/// <summary>
			/// The RUN command.
			/// </summary>
			RUN = ((byte)0x50),
			/// <summary>
			/// The STEP_CLOCK command.
			/// </summary>
			STEP_CLOCK = ((byte)0x58),
			/// <summary>
			/// The MOVE command.
			/// </summary>
			MOVE = ((byte)0x40),
			/// <summary>
			/// The GO_TO command.
			/// </summary>
			GO_TO = ((byte)0x60),
			/// <summary>
			/// The GO_TO_DIR command.
			/// </summary>
			GO_TO_DIR = ((byte)0x68),
			/// <summary>
			/// The GO_UNTIL command.
			/// </summary>
			GO_UNTIL = ((byte)0x82),
			/// <summary>
			/// The RELEASE_SW command.
			/// </summary>
			RELEASE_SW = ((byte)0x92),
			/// <summary>
			/// The GO_HOME command.
			/// </summary>
			GO_HOME = ((byte)0x70),
			/// <summary>
			/// The GO_MARK command.
			/// </summary>
			GO_MARK = ((byte)0x78),
			/// <summary>
			/// The RESET_POS command.
			/// </summary>
			RESET_POS = ((byte)0xD8),
			/// <summary>
			/// The RESET_DEVICE command.
			/// </summary>
			RESET_DEVICE = ((byte)0xC0),
			/// <summary>
			/// The SOFT_STOP command.
			/// </summary>
			SOFT_STOP = ((byte)0xB0),
			/// <summary>
			/// The HARD_STOP command.
			/// </summary>
			HARD_STOP = ((byte)0xB8),
			/// <summary>
			/// The SOFT_HIZ command.
			/// </summary>
			SOFT_HIZ = ((byte)0xA0),
			/// <summary>
			/// The HARD_HIZ command.
			/// </summary>
			HARD_HIZ = ((byte)0xA8),
			/// <summary>
			/// The GET_STATUS command.
			/// </summary>
			GET_STATUS = ((byte)0xD0),
			/// <summary>
			/// The RESERVED_CMD1 command.
			/// </summary>
			RESERVED_CMD1 = ((byte)0xEB),
			/// <summary>
			/// The RESERVED_CMD2 command.
			/// </summary>
            RESERVED_CMD2 = ((byte)0xF8)
        };


        private void Regs_Struct_Reset(RegsStruct RegsStruct)
        {
            RegsStruct.ABS_POS = 0;
            RegsStruct.EL_POS = 0;
            RegsStruct.MARK = 0;
            RegsStruct.SPEED = 0;
            RegsStruct.ACC = 0x08A;
            RegsStruct.DEC = 0x08A;
            RegsStruct.MAX_SPEED = 0x041;
            RegsStruct.MIN_SPEED = 0;
            RegsStruct.FS_SPD = 0x027;
            RegsStruct.KVAL_HOLD = 0x29;
            RegsStruct.KVAL_RUN = 0x29;
            RegsStruct.KVAL_ACC = 0x29;
            RegsStruct.KVAL_DEC = 0x29;
            RegsStruct.INT_SPD = 0x0408;
            RegsStruct.ST_SLP = 0x19;
            RegsStruct.FN_SLP_ACC = 0x29;
            RegsStruct.FN_SLP_DEC = 0x29;
            RegsStruct.K_THERM = 0;
            RegsStruct.OCD_TH = 0x8;
            RegsStruct.STALL_TH = 0x40;
            RegsStruct.STEP_MODE = 0x7;
            RegsStruct.ALARM_EN = 0xFF;
            RegsStruct.CONFIG = 0x2E88;
        }

        /// <summary>
        /// Sets the registers to the passed in register values
        /// </summary>
        /// <param name="RegsStruct">Register values to set the values to</param>
        public void SetRegisters(RegsStruct RegsStruct)
        {
            SetParam(Registers.ABS_POS, RegsStruct.ABS_POS);
            SetParam(Registers.EL_POS, RegsStruct.EL_POS);
            SetParam(Registers.MARK, RegsStruct.MARK);
            SetParam(Registers.SPEED, RegsStruct.SPEED);
            SetParam(Registers.ACC, RegsStruct.ACC);
            SetParam(Registers.DEC, RegsStruct.DEC);
            SetParam(Registers.MAX_SPEED, RegsStruct.MAX_SPEED);
            SetParam(Registers.MIN_SPEED, RegsStruct.MIN_SPEED);
            SetParam(Registers.FS_SPD, RegsStruct.FS_SPD);
            SetParam(Registers.KVAL_HOLD, RegsStruct.KVAL_HOLD);
            SetParam(Registers.KVAL_RUN, RegsStruct.KVAL_RUN);
            SetParam(Registers.KVAL_ACC, RegsStruct.KVAL_ACC);
            SetParam(Registers.KVAL_DEC, RegsStruct.KVAL_DEC);
            SetParam(Registers.INT_SPD, RegsStruct.INT_SPD);
            SetParam(Registers.ST_SLP, RegsStruct.ST_SLP);
            SetParam(Registers.FN_SLP_ACC, RegsStruct.FN_SLP_ACC);
            SetParam(Registers.FN_SLP_DEC, RegsStruct.FN_SLP_DEC);
            SetParam(Registers.K_THERM, RegsStruct.K_THERM);
            SetParam(Registers.OCD_TH, RegsStruct.OCD_TH);
            SetParam(Registers.STALL_TH, RegsStruct.STALL_TH);
            SetParam(Registers.STEP_MODE, RegsStruct.STEP_MODE);
            SetParam(Registers.ALARM_EN, RegsStruct.ALARM_EN);
            SetParam(Registers.CONFIG, RegsStruct.CONFIG);
        }

        /// <summary>
        /// Overcurrent Detection Threshold Options
        /// </summary>
        public enum Overcurrent_Detection_Threshold
        {
			/// <summary>
			/// The 375mA option.
			/// </summary>
			OCD_TH_375mA = ((byte)0x00),
			/// <summary>
			/// The 750mA option.
			/// </summary>
			OCD_TH_750mA = ((byte)0x01),
			/// <summary>
			/// The 1125mA option.
			/// </summary>
			OCD_TH_1125mA = ((byte)0x02),
			/// <summary>
			/// The 1500mA option.
			/// </summary>
			OCD_TH_1500mA = ((byte)0x03),
			/// <summary>
			/// The 1875mA option.
			/// </summary>
			OCD_TH_1875mA = ((byte)0x04),
			/// <summary>
			/// The 2250mA option.
			/// </summary>
			OCD_TH_2250mA = ((byte)0x05),
			/// <summary>
			/// The 2625mA option.
			/// </summary>
			OCD_TH_2625mA = ((byte)0x06),
			/// <summary>
			/// The 3000mA option.
			/// </summary>
			OCD_TH_3000mA = ((byte)0x07),
			/// <summary>
			/// The 3375mA option.
			/// </summary>
			OCD_TH_3375mA = ((byte)0x08),
			/// <summary>
			/// The 3750mA option.
			/// </summary>
			OCD_TH_3750mA = ((byte)0x09),
			/// <summary>
			/// The 4125mA option.
			/// </summary>
			OCD_TH_4125mA = ((byte)0x0A),
			/// <summary>
			/// The 4500mA option.
			/// </summary>
			OCD_TH_4500mA = ((byte)0x0B),
			/// <summary>
			/// The 4875mA option.
			/// </summary>
			OCD_TH_4875mA = ((byte)0x0C),
			/// <summary>
			/// The 5250mA option.
			/// </summary>
			OCD_TH_5250mA = ((byte)0x0D),
			/// <summary>
			/// The 5625mA option.
			/// </summary>
			OCD_TH_5625mA = ((byte)0x0E),
			/// <summary>
			/// The 6000mA option.
			/// </summary>
            OCD_TH_6000mA = ((byte)0x0F)
        };

        /// <summary>
        /// Step Select Options
        /// </summary>
        public enum Step_Select
        {
			/// <summary>
			/// The step 1 option.
			/// </summary>
			STEP_SEL_1 = ((byte)0x00),
			/// <summary>
			/// The step 2 option.
			/// </summary>
			STEP_SEL_1_2 = ((byte)0x01),
			/// <summary>
			/// The step 4 option.
			/// </summary>
			STEP_SEL_1_4 = ((byte)0x02),
			/// <summary>
			/// The step 8 option.
			/// </summary>
			STEP_SEL_1_8 = ((byte)0x03),
			/// <summary>
			/// The step 16 option.
			/// </summary>
			STEP_SEL_1_16 = ((byte)0x04),
			/// <summary>
			/// The step 32 option.
			/// </summary>
			STEP_SEL_1_32 = ((byte)0x05),
			/// <summary>
			/// The step 64 option.
			/// </summary>
			STEP_SEL_1_64 = ((byte)0x06),
			/// <summary>
			/// The step 128 option.
			/// </summary>
            STEP_SEL_1_128 = ((byte)0x07)
        };

        /// <summary>
        /// Alarm Enable Options
        /// </summary>
        public enum Alarm_Enable
        {
			/// <summary>
			/// The overcurrent option.
			/// </summary>
			ALARM_EN_OVERCURRENT = ((byte)0x01),
			/// <summary>
			/// The thermal shutdown option.
			/// </summary>
			ALARM_EN_THERMAL_SHUTDOWN = ((byte)0x02),
			/// <summary>
			/// The thermal warning option.
			/// </summary>
			ALARM_EN_THERMAL_WARNING = ((byte)0x04),
			/// <summary>
			/// The under voltage option.
			/// </summary>
			ALARM_EN_UNDER_VOLTAGE = ((byte)0x08),
			/// <summary>
			/// The overcurrent option.
			/// </summary>
			ALARM_EN_STALL_DET_A = ((byte)0x10),
			/// <summary>
			/// The stall detection A option.
			/// </summary>
			ALARM_EN_STALL_DET_B = ((byte)0x20),
			/// <summary>
			/// The stall detection B option.
			/// </summary>
			ALARM_EN_SW_TURN_ON = ((byte)0x40),
			/// <summary>
			/// The wrong nperf option.
			/// </summary>
            ALARM_EN_WRONG_NPERF_CMD = ((byte)0x80)
        };

        /// <summary>
        /// Configuration Register Options
        /// </summary>
        public enum CONFIG_OSC_MGMT
		{
			/// <summary>
			/// The 16MHz option.
			/// </summary>
			CONFIG_INT_16MHZ = ((ushort)0x0000),
			/// <summary>
			/// The 16MHz OSCOUNT 2MHz option.
			/// </summary>
			CONFIG_INT_16MHZ_OSCOUT_2MHZ = ((ushort)0x0008),
			/// <summary>
			/// The 16MHz OSCOUNT 4MHz option.
			/// </summary>
			CONFIG_INT_16MHZ_OSCOUT_4MHZ = ((ushort)0x0009),
			/// <summary>
			/// The 16MHz OSCOUNT 8MHz option.
			/// </summary>
			CONFIG_INT_16MHZ_OSCOUT_8MHZ = ((ushort)0x000A),
			/// <summary>
			/// The 16MHz OSCOUNT 16MHz option.
			/// </summary>
			CONFIG_INT_16MHZ_OSCOUT_16MHZ = ((ushort)0x000B),
			/// <summary>
			/// The 8MHz XTAL drive option.
			/// </summary>
			CONFIG_EXT_8MHZ_XTAL_DRIVE = ((ushort)0x0004),
			/// <summary>
			/// The 16MHz XTAL drive option.
			/// </summary>
			CONFIG_EXT_16MHZ_XTAL_DRIVE = ((ushort)0x0005),
			/// <summary>
			/// The 24MHz XTAL drive option.
			/// </summary>
			CONFIG_EXT_24MHZ_XTAL_DRIVE = ((ushort)0x0006),
			/// <summary>
			/// The 32MHz XTAL drive option.
			/// </summary>
			CONFIG_EXT_32MHZ_XTAL_DRIVE = ((ushort)0x0007),
			/// <summary>
			/// The 8MHz OSCOUNT invert option.
			/// </summary>
			CONFIG_EXT_8MHZ_OSCOUT_INVERT = ((ushort)0x000C),
			/// <summary>
			/// The 16MHz OSCOUNT invert option.
			/// </summary>
			CONFIG_EXT_16MHZ_OSCOUT_INVERT = ((ushort)0x000D),
			/// <summary>
			/// The 24MHz OSCOUNT invert option.
			/// </summary>
			CONFIG_EXT_24MHZ_OSCOUT_INVERT = ((ushort)0x000E),
			/// <summary>
			/// The 32MHz OSCOUNT invert option.
			/// </summary>
            CONFIG_EXT_32MHZ_OSCOUT_INVERT = ((ushort)0x000F)
        };

        /// <summary>
        /// The SW Mode configuration.
        /// </summary>
        public enum CONFIG_SW_MODE_TypeDef
		{
			/// <summary>
			/// The hard stop option.
			/// </summary>
			CONFIG_SW_HARD_STOP = ((ushort)0x0000),
			/// <summary>
			/// The user option.
			/// </summary>
            CONFIG_SW_USER = ((ushort)0x0010)
        };

        /// <summary>
        /// The PWM configuration options.
        /// </summary>
        public enum CONFIG_F_PWM_DEC
		{
			/// <summary>
			/// The 0-625 option.
			/// </summary>
			CONFIG_PWM_MUL_0_625 = (((ushort)0x00) << 10),
			/// <summary>
			/// The 0-75 option.
			/// </summary>
			CONFIG_PWM_MUL_0_75 = (((ushort)0x01) << 10),
			/// <summary>
			/// The 0-875 option.
			/// </summary>
			CONFIG_PWM_MUL_0_875 = (((ushort)0x02) << 10),
			/// <summary>
			/// The 1 option.
			/// </summary>
			CONFIG_PWM_MUL_1 = (((ushort)0x03) << 10),
			/// <summary>
			/// The 1-25 option.
			/// </summary>
			CONFIG_PWM_MUL_1_25 = (((ushort)0x04) << 10),
			/// <summary>
			/// The 1-5 option.
			/// </summary>
			CONFIG_PWM_MUL_1_5 = (((ushort)0x05) << 10),
			/// <summary>
			/// The 1-75 option.
			/// </summary>
			CONFIG_PWM_MUL_1_75 = (((ushort)0x06) << 10),
			/// <summary>
			/// The 2 option.
			/// </summary>
            CONFIG_PWM_MUL_2 = (((ushort)0x07) << 10)
        };
        
        /// <summary>
        /// The PWM INT configuration options.
        /// </summary>
        public enum CONFIG_F_PWM_INT
		{
			/// <summary>
			/// The DIV 1 option.
			/// </summary>
			CONFIG_PWM_DIV_1 = (((ushort)0x00) << 13),
			/// <summary>
			/// The DIV 2 option.
			/// </summary>
			CONFIG_PWM_DIV_2 = (((ushort)0x01) << 13),
			/// <summary>
			/// The DIV 3 option.
			/// </summary>
			CONFIG_PWM_DIV_3 = (((ushort)0x02) << 13),
			/// <summary>
			/// The DIV 4 option.
			/// </summary>
			CONFIG_PWM_DIV_4 = (((ushort)0x03) << 13),
			/// <summary>
			/// The DIV 5 option.
			/// </summary>
			CONFIG_PWM_DIV_5 = (((ushort)0x04) << 13),
			/// <summary>
			/// The DIV 6 option.
			/// </summary>
			CONFIG_PWM_DIV_6 = (((ushort)0x05) << 13),
			/// <summary>
			/// The DIV 7 option.
			/// </summary>
            CONFIG_PWM_DIV_7 = (((ushort)0x06) << 13)
        };

        /// <summary>
        /// The POW SR configuration options.
        /// </summary>
        public enum CONFIG_POW_SR
		{
			/// <summary>
			/// The 180V option.
			/// </summary>
			CONFIG_SR_180V_us = ((ushort)0x0000),
			/// <summary>
			/// The 290V option.
			/// </summary>
			CONFIG_SR_290V_us = ((ushort)0x0200),
			/// <summary>
			/// The 530V option.
			/// </summary>
            CONFIG_SR_530V_us = ((ushort)0x0300)
        };

        /// <summary>
        /// The EN VSCOMP configuration options.
        /// </summary>
        public enum CONFIG_EN_VSCOMP
		{
			/// <summary>
			/// The disable option.
			/// </summary>
			CONFIG_VS_COMP_DISABLE = ((ushort)0x0000),
			/// <summary>
			/// The enable option.
			/// </summary>
            CONFIG_VS_COMP_ENABLE = ((ushort)0x0020)
        };

        /// <summary>
        /// The OC configuration options.
        /// </summary>
        public enum CONFIG_OC_SD
		{
			/// <summary>
			/// The disable option.
			/// </summary>
			CONFIG_OC_SD_DISABLE = ((ushort)0x0000),
			/// <summary>
			/// The enable option.
			/// </summary>
            CONFIG_OC_SD_ENABLE = ((ushort)0x0080)
        };

		/// <summary>
		/// Converts speed steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public uint Speed_Steps_to_Par(uint steps)
        {
            return ((uint)(((steps) * 67.108864) + 0.5));
        }
		/// <summary>
		/// Converts AccDec steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public ushort AccDec_Steps_to_Par(uint steps)
        {
            return ((ushort)(((steps) * 0.068719476736) + 0.5));
        }
		/// <summary>
		/// Converts max speed steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public ushort MaxSpd_Steps_to_Par(uint steps)
        {
            return ((ushort)(((steps) * 0.065536) + 0.5));
        }
		/// <summary>
		/// Converts min speed steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public ushort MinSpd_Steps_to_Par(uint steps)
        {
            return ((ushort)(((steps) * 4.194304) + 0.5));
        }
		/// <summary>
		/// Converts FS speed steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public ushort FSSpd_Steps_to_Par(uint steps)
        {
            return (ushort)(((steps) * 0.065536));
        }
		/// <summary>
		/// Converts int speed steps to par.
		/// </summary>
		/// <param name="steps">The steps.</param>
		/// <returns>Steps expressed as par.</returns>
        public ushort IntSpd_Steps_to_Par(uint steps)
        {
            return ((ushort)(((steps) * 4.194304) + 0.5));
        }
		/// <summary>
		/// Converts Kval percent to par.
		/// </summary>
		/// <param name="perc">The percent.</param>
		/// <returns>Percent expressed as par.</returns>
        public byte Kval_Perc_to_Par(float perc)
        {
            return ((byte)(((perc) / 0.390625) + 0.5));
        }
		/// <summary>
		/// Converts BEMF slope percent to par.
		/// </summary>
		/// <param name="perc">The percent.</param>
		/// <returns>Percent expressed as par.</returns>
        public byte BEMF_Slope_Perc_to_Par(float perc)
        {
            return ((byte)(((perc) / 0.00156862745098) + 0.5));
        }
		/// <summary>
		/// Converts KTherm to par.
		/// </summary>
		/// <param name="KTherm">The percent.</param>
		/// <returns>KTherms expressed as par.</returns>
        public byte KTherm_to_Par(uint KTherm)
        {
            return ((byte)(((KTherm - 1) / 0.03125) + 0.5));
        }
		/// <summary>
		/// Converts StallTH to par.
		/// </summary>
		/// <param name="StallTh">The StallTh.</param>
		/// <returns>StallTh expressed as par.</returns>
        public byte StallTh_to_Par(uint StallTh)
        {
            return ((byte)(((StallTh - 31.25) / 31.25) + 0.5));
        }
    } 

    /// <summary>
    /// Avalilable Registers
    /// </summary>
    public class RegsStruct
    {
        /// <summary>
        /// Empty Constructor
        /// </summary>
        public RegsStruct()
        {
            //
        }

		/// <summary>
		/// The ABS_POS register.
		/// </summary>
		public uint ABS_POS;
		/// <summary>
		/// The EL_POS register.
		/// </summary>
		public ushort EL_POS;
		/// <summary>
		/// The MARK register.
		/// </summary>
		public uint MARK;
		/// <summary>
		/// The SPEED register.
		/// </summary>
		public uint SPEED;
		/// <summary>
		/// The ACC register.
		/// </summary>
		public ushort ACC;
		/// <summary>
		/// The DEC register.
		/// </summary>
		public ushort DEC;
		/// <summary>
		/// The MAX_SPEED register.
		/// </summary>
		public ushort MAX_SPEED;
		/// <summary>
		/// The MIN_SPEED register.
		/// </summary>
		public ushort MIN_SPEED;
		/// <summary>
		/// The FS_SPD register.
		/// </summary>
		public ushort FS_SPD;
		/// <summary>
		/// The KVAL_HOLD register.
		/// </summary>
		public byte KVAL_HOLD;
		/// <summary>
		/// The KVAL_RUN register.
		/// </summary>
		public byte KVAL_RUN;
		/// <summary>
		/// The KVAL_ACC register.
		/// </summary>
		public byte KVAL_ACC;
		/// <summary>
		/// The KVAL_DEC register.
		/// </summary>
		public byte KVAL_DEC;
		/// <summary>
		/// The INT_SPD register.
		/// </summary>
		public ushort INT_SPD;
		/// <summary>
		/// The ST_SLP register.
		/// </summary>
		public byte ST_SLP;
		/// <summary>
		/// The FN_SLP_ACC register.
		/// </summary>
		public byte FN_SLP_ACC;
		/// <summary>
		/// The FN_SLP_DEC register.
		/// </summary>
		public byte FN_SLP_DEC;
		/// <summary>
		/// The K_THERM register.
		/// </summary>
		public byte K_THERM;
		/// <summary>
		/// The ADC_OUT register.
		/// </summary>
		public byte ADC_OUT;
		/// <summary>
		/// The OCD_TH register.
		/// </summary>
		public byte OCD_TH;
		/// <summary>
		/// The STALL_TH register.
		/// </summary>
		public byte STALL_TH;
		/// <summary>
		/// The STEP_MODE register.
		/// </summary>
		public byte STEP_MODE;
		/// <summary>
		/// The ALARM_EN register.
		/// </summary>
		public byte ALARM_EN;
		/// <summary>
		/// The CONFIG register.
		/// </summary>
        public ushort CONFIG;
    }
}
